use std::{fs, path::Path};

use axum::extract::{Query, State};

use serde::Deserialize;

use crate::{
    mp,
    stores::{self, redisx, sql},
    utils, Result, SharedState,
};

/// redis中的key编码，vcode 验证码 icode 识别码 ecode 核验码

#[derive(Debug, Deserialize)]
pub struct MpGetParams {
    pub signature: String,
    pub timestamp: String,
    pub nonce: String,
    pub echostr: String,
}

// 设置公众号后台URL时，微信平台验证
pub async fn get_mp(Query(params): Query<MpGetParams>, State(state): State<SharedState>) -> String {
    let conf = &state.conf;
    let ok = mp::check_mp_sign(
        params.signature,
        conf.mp.token.clone(),
        params.timestamp,
        params.nonce,
    );
    if ok {
        return params.echostr;
    }
    "".to_string()
}

#[derive(Debug, Deserialize)]
pub struct MpPostParams {
    pub signature: String,
    pub timestamp: String,
    pub nonce: String,
    pub openid: String,
}
// 微信平台推送消息入口
pub async fn post_mp(
    State(state): State<SharedState>,
    Query(params): Query<MpPostParams>,
    body: String,
) -> Result<String> {
    let conf = &state.conf;
    // 验证签名，可以判断消息是否来自微信平台
    let ok = mp::check_mp_sign(
        params.signature,
        conf.mp.token.clone(),
        params.timestamp,
        params.nonce,
    );
    if ok {
        // 消息来自微信平台，处理消息
        // if conf.mp.is_encrypt {}
        let msg = mp::Message::parse(body);
        match msg {
            mp::Message::TextMessage(m) => {
                if m.content.as_str() == "验证码" {
                    let pool = &state.db;
                    let cnt = sql::query_user_count(&pool, params.openid.clone()).await?;
                    if cnt == 0 {
                        let rand_str = utils::get_rand_str(6);
                        let nickname = format!("mu{}", rand_str.to_lowercase());
                        sql::create_user(&pool, params.openid.clone(), nickname).await?;
                    }

                    let from_user_name = m.from_user_name;
                    let to_user_name = m.to_user_name;
                    let ts = utils::get_timestamp();
                    let code = utils::get_rand_num(6);
                    let rcode = format!("vcode-{}", code);
                    let content = format!("验证码：{}，5分钟内有效", code);
                    let client = &state.rs;
                    redisx::set_ex(client, rcode, from_user_name.clone(), 300).await?;
                    let r = mp::TextReply {
                        from_user_name: to_user_name,
                        to_user_name: from_user_name,
                        create_time: ts,
                        content: content,
                    };

                    let rr = mp::Reply::TextReply(r).render();

                    return Ok(rr);
                }
                let text_msg = m.content.as_str();
                if text_msg.contains("识别码") {
                    let from_user_name = m.from_user_name;
                    let to_user_name = m.to_user_name;
                    let ts = utils::get_timestamp();
                    // 是否包含|，英文和中文相同，格式为识别码|文本内容
                    if text_msg.contains("|") {
                        let msg_arr: Vec<&str> = text_msg.split("|").collect();
                        if msg_arr.len() != 2 {
                            let content = String::from(
                                "命令格式不正确，请输入识别码|内容，内容中不能包含'|'分隔符",
                            );
                            let r = mp::TextReply {
                                from_user_name: to_user_name,
                                to_user_name: from_user_name,
                                create_time: ts,
                                content: content,
                            };
                            let rr = mp::Reply::TextReply(r).render();
                            return Ok(rr);
                        } else {
                            let msg1 = msg_arr[1];
                            if msg1.len() > 40 {
                                let content =
                                    String::from("内容超过最大限制40字符，请重新输入内容");
                                let r = mp::TextReply {
                                    from_user_name: to_user_name,
                                    to_user_name: from_user_name,
                                    create_time: ts,
                                    content: content,
                                };
                                let rr = mp::Reply::TextReply(r).render();
                                return Ok(rr);
                            } else {
                                let code = utils::get_rand_num(8);
                                let icode_key = format!("icode-{}", code);
                                let icode_str = format!("{}{}", code, ts);
                                let md5code = utils::md516(&icode_str);
                                let user_content = String::from(msg1);
                                let client = &state.rs;
                                redisx::set_ex(client, icode_key, md5code.clone(), 24 * 60 * 60)
                                    .await?;
                                redisx::set_ex(client, md5code.clone(), user_content, 24 * 60 * 60)
                                    .await?;

                                // 需要生成小程序码
                                let wander_key = String::from("wander-token");
                                let mut wander_token = redisx::get(client, &wander_key)
                                    .await
                                    .unwrap_or("".to_string());
                                if wander_token == "" {
                                    let conf = &state.conf;
                                    let token = utils::get_mp_access_token(
                                        &conf.mp.app_id,
                                        &conf.mp.app_secret,
                                    )
                                    .await?;
                                    redisx::set_ex(client, wander_key, token.clone(), 7200).await?;
                                    wander_token = token;
                                }
                                let scene = format!("a={}&b={}", "1002", md5code);
                                let qrcode =
                                    utils::get_miniapp_qrcode(&wander_token, scene).await?;
                                let oimg = String::from("tmp/mp_qrcode.png");
                                let qb = Vec::from(qrcode);
                                utils::draw_mp_image(qb, code.clone(), &oimg)?;
                                // 检查图片文件是否存在？存在就上传素材到后台，删除本地文件
                                let oimg_path = Path::new(&oimg);
                                let existed = oimg_path.exists();
                                if existed {
                                    let gzh_key = String::from("gzh-token");
                                    let mut gzh_token = redisx::get(client, &gzh_key)
                                        .await
                                        .unwrap_or("".to_string());
                                    if gzh_token == "" {
                                        let conf = &state.conf;
                                        let token = utils::get_mp_access_token(
                                            &conf.mp.gzh_app_id,
                                            &conf.mp.gzh_app_secret,
                                        )
                                        .await?;
                                        redisx::set_ex(client, gzh_key, token.clone(), 7200)
                                            .await?;
                                        gzh_token = token;
                                    }

                                    let media_id =
                                        utils::upload_gzh_image(&gzh_token, &oimg).await?;
                                    let _ = fs::remove_file(oimg_path);

                                    let r = mp::ImageReply {
                                        from_user_name: to_user_name,
                                        to_user_name: from_user_name,
                                        create_time: ts,
                                        media_id,
                                    };
                                    let rr = mp::Reply::ImageReply(r).render();
                                    return Ok(rr);
                                } else {
                                    // 如果没有图片，则使用文本
                                    let content = format!(
                                        "识别码：{}，可搜索微信小程序[爱上随机数]进行体验。",
                                        code
                                    );

                                    let r = mp::TextReply {
                                        from_user_name: to_user_name,
                                        to_user_name: from_user_name,
                                        create_time: ts,
                                        content: content,
                                    };
                                    let rr = mp::Reply::TextReply(r).render();
                                    return Ok(rr);
                                }
                            }
                        }
                    } else {
                        let mut content = String::new();
                        content.push_str("识别码是一串长度为8的数字，可用它来识别用户信息。它是一次性码，用完即作废。\n\n它有两种使用方式：\n一、使用本公众号的消息模式，二、使用接口模式。\n");
                        content.push_str("\n一、消息模式，需在公众号内发送如下格式的消息：识别码|内容，其中识别码为固定字符串，|是分隔符，内容为自定义字符串，您可填写这个识别码要展现的内容，内容最大长度为40个字符。系统会返回一个包含识别码文本和小程序码的图片，扫描小程序码可以直接展示您提交的内容，或打开[爱上随机数]小程序，手动填写识别码查看提交的内容。\n");
                        content.push_str("\n二、接口模式，可调用接口实现识别码的生成和验证，有编程基础的感兴趣读者可以查看接口文档\nhttps://libs.91demo.top/apidoc.html，\n使用您喜爱的编程语言来进行体验^_^。\n");
                        content.push_str("\n这项功能仅为演示功能，用于配合技术文章的功能完整性。");
                        let r = mp::TextReply {
                            from_user_name: to_user_name,
                            to_user_name: from_user_name,
                            create_time: ts,
                            content: content,
                        };

                        let rr = mp::Reply::TextReply(r).render();

                        return Ok(rr);
                    }
                }
                if text_msg.contains("核验码") {
                    let from_user_name = m.from_user_name;
                    let to_user_name = m.to_user_name;
                    let ts = utils::get_timestamp();
                    let mut content = String::new();
                    content.push_str("核验码是一串长度为8的字符串，它在申请账号时核验使用。它是一次性码，用完即作废。此账号为开发者账号，在识别码中，如果使用接口模式开发，则需要该账号先生成access token，才能进行业务接口的调用。\n\n账号申请有两种方式：\n一、使用小程序进行申请，二、使用邮箱进行申请。\n");
                    content.push_str("\n一、小程序申请账号，需在公众号内发送如下格式的消息：小程序申请账号，系统会返回一个包含核验码的文本，请打开[爱上随机数]小程序，使用核验码功能申请账号。\n");
                    content.push_str("\n二、邮箱申请账号，需在公众号内发送如下格式的消息：邮箱申请账号|邮箱地址，系统会往邮箱发送申请链接和包含核验码的文本。点击链接即可申请账号，或使用核验码去小程序申请\n");
                    content.push_str("\n这项功能仅为演示功能，用于配合技术文章的功能完整性。");
                    let r = mp::TextReply {
                        from_user_name: to_user_name,
                        to_user_name: from_user_name,
                        create_time: ts,
                        content: content,
                    };
                    let rr = mp::Reply::TextReply(r).render();
                    return Ok(rr);
                }
                if text_msg == "小程序申请账号" {
                    // TODO 实现功能，这里对应用户的openid，然后生成两个字段，分别为appid和appsecret，然后插入到数据库中。
                    let from_user_name = m.from_user_name;
                    let to_user_name = m.to_user_name;
                    let ts = utils::get_timestamp();
                    let ecode = utils::get_rand_str(8);
                    let ecode_key = format!("ecode-{}", ecode);
                    let openid = from_user_name.clone();
                    let pool = &state.db;
                    if let Ok(m) = stores::sql::get_appaccount_by_openid(pool, openid.clone()).await
                    {
                        tracing::error!("apply app account error,appid {} is existed", m.appid);
                        let content = format!(
                            "您已经申请过账号，若需重置账号密钥，请在公众号消息中输入：重置账号密钥|您的邮箱，警告重置账号密钥后，原密钥将不可使用，请谨慎操作，您的账号为：{}",
                             m.appid
                        );
                        let r = mp::TextReply {
                            from_user_name: to_user_name,
                            to_user_name: from_user_name,
                            create_time: ts,
                            content: content,
                        };
                        let rr = mp::Reply::TextReply(r).render();
                        return Ok(rr);
                    }

                    let client = &state.rs;
                    redisx::set_ex(client, ecode_key, openid, 24 * 60 * 60).await?;
                    let content = format!(
                        "您的核验码是{}，24小时之内有效，请打开微信小程序[爱上随机数]进行账号申请",
                        ecode
                    );
                    let r = mp::TextReply {
                        from_user_name: to_user_name,
                        to_user_name: from_user_name,
                        create_time: ts,
                        content: content,
                    };
                    let rr = mp::Reply::TextReply(r).render();
                    return Ok(rr);
                }
                if text_msg.starts_with("邮箱申请账号") {
                    if text_msg.contains("|") {
                        let msg_arr: Vec<&str> = text_msg.split("|").collect();
                        if msg_arr.len() == 2 {
                            let email = msg_arr[1];
                            if email.contains("@") {
                                // 生成核验码，然后发送邮件
                                let from_user_name = m.from_user_name;
                                let to_user_name = m.to_user_name;
                                let ts = utils::get_timestamp();
                                let openid = from_user_name.clone();
                                let conf = &state.conf;
                                let subject = String::from("Rabbit账号申请");
                                let db = &state.db;
                                if let Ok(m) =
                                    stores::sql::get_appaccount_by_openid(db, openid.clone()).await
                                {
                                    let email_content =
                                    format!("您已经申请过账号，若需重置账号密钥，请在公众号消息中输入：重置账号密钥|您的邮箱，警告重置账号密钥后，原密钥将不可使用，请谨慎操作，您的账号为：{}。", m.appid);
                                    utils::send_email(
                                        &conf.email,
                                        email.to_owned(),
                                        subject,
                                        email_content,
                                    )?;
                                    let content = String::from(
                                        "邮件发送成功，邮件标题为Rabbit账号申请，您已经申请过账号，详情内容请查看邮件",
                                        );

                                    let r = mp::TextReply {
                                        from_user_name: to_user_name,
                                        to_user_name: from_user_name,
                                        create_time: ts,
                                        content: content,
                                    };
                                    let rr = mp::Reply::TextReply(r).render();
                                    return Ok(rr);
                                }
                                let ecode = utils::get_rand_str(8);
                                let ecode_key = format!("ecode-{}", ecode);

                                let client = &state.rs;
                                redisx::set_ex(client, ecode_key, openid, 24 * 60 * 60).await?;
                                // 设置邮件

                                let c = utils::gen_ecode_str(&ecode, ts, &state.conf.mp.shared_key);
                                let apply_account_url = format!(
                                    "https://libs.91demo.top/applyappaccount?a={}&b={}&c={}",
                                    ecode, ts, c
                                );
                                let email_content =
                                    format!("您的核验码为{}，可打开微信小程序[爱上随机数]申请账号。也可使用下面的链接地址在浏览器中直接打开申请账号，链接地址：{}", ecode,apply_account_url);
                                utils::send_email(
                                    &conf.email,
                                    email.to_owned(),
                                    subject,
                                    email_content,
                                )?;
                                let content = String::from(
                                    "邮件发送成功，邮件标题为Rabbit账号申请，核验码24小时之内有效，请打开邮箱进行账号申请",
                                    );

                                let r = mp::TextReply {
                                    from_user_name: to_user_name,
                                    to_user_name: from_user_name,
                                    create_time: ts,
                                    content: content,
                                };
                                let rr = mp::Reply::TextReply(r).render();
                                return Ok(rr);
                            }
                        }
                    }
                    let content =
                        String::from("命令格式不正确，请输入如下格式：邮箱申请账号|您的邮箱地址");
                    let from_user_name = m.from_user_name;
                    let to_user_name = m.to_user_name;
                    let ts = utils::get_timestamp();

                    let r = mp::TextReply {
                        from_user_name: to_user_name,
                        to_user_name: from_user_name,
                        create_time: ts,
                        content: content,
                    };
                    let rr = mp::Reply::TextReply(r).render();
                    return Ok(rr);
                }
                if text_msg.starts_with("重置账号密钥") {
                    if text_msg.contains("|") {
                        let msg_arr: Vec<&str> = text_msg.split("|").collect();
                        if msg_arr.len() == 2 {
                            let email = msg_arr[1];
                            if email.contains("@") {
                                let from_user_name = m.from_user_name;
                                let to_user_name = m.to_user_name;
                                let ts = utils::get_timestamp();
                                let openid = from_user_name.clone();
                                let conf = &state.conf;
                                let subject = String::from("Rabbit重置账号密钥");
                                let db = &state.db;
                                if let Ok(m) =
                                    stores::sql::get_appaccount_by_openid(db, openid.clone()).await
                                {
                                    let app_secret = utils::reset_app_account_secret();
                                    stores::sql::reset_appaccount_secret_by_appid(
                                        db,
                                        m.appid.clone(),
                                        app_secret.clone(),
                                    )
                                    .await?;
                                    let email_content =
                                    format!("重置账号密钥成功，您的账号为：{}，密钥为：{}，请妥善保管！", m.appid,app_secret);
                                    utils::send_email(
                                        &conf.email,
                                        email.to_owned(),
                                        subject,
                                        email_content,
                                    )?;
                                    let content = String::from(
                                        "邮件发送成功，邮件标题为Rabbit重置账号密钥，重置账号密钥操作成功，详情内容请查看邮件",
                                        );

                                    let r = mp::TextReply {
                                        from_user_name: to_user_name,
                                        to_user_name: from_user_name,
                                        create_time: ts,
                                        content: content,
                                    };
                                    let rr = mp::Reply::TextReply(r).render();
                                    return Ok(rr);
                                } else {
                                    let content = String::from(
                                        "您还未申请过账号，请先申请账号，在公众号内输入：核验码，可查看使用方法",
                                        );

                                    let r = mp::TextReply {
                                        from_user_name: to_user_name,
                                        to_user_name: from_user_name,
                                        create_time: ts,
                                        content: content,
                                    };
                                    let rr = mp::Reply::TextReply(r).render();
                                    return Ok(rr);
                                }
                            }
                        }
                    }
                    let content =
                        String::from("命令格式不正确，请输入如下格式：邮箱申请账号|您的邮箱地址");
                    let from_user_name = m.from_user_name;
                    let to_user_name = m.to_user_name;
                    let ts = utils::get_timestamp();

                    let r = mp::TextReply {
                        from_user_name: to_user_name,
                        to_user_name: from_user_name,
                        create_time: ts,
                        content: content,
                    };
                    let rr = mp::Reply::TextReply(r).render();
                    return Ok(rr);
                }
                if m.content.as_str().starts_with("图书审核-") {
                    let from_user_name = m.from_user_name;
                    let to_user_name = m.to_user_name;
                    let ts = utils::get_timestamp();
                    // 提取审核码
                    let str1: Vec<&str> = m.content.as_str().split("-").collect();
                    if str1.len() != 3 {
                        let content = "命令格式不正确，请输入 图书审核-审核码-状态，其中状态为：1，为通过；2，为不通过";
                        let r = mp::TextReply {
                            from_user_name: to_user_name,
                            to_user_name: from_user_name,
                            create_time: ts,
                            content: content.to_owned(),
                        };

                        let rr = mp::Reply::TextReply(r).render();

                        return Ok(rr);
                    }
                    let acode = str1[1];
                    let status = str1[2].parse::<i32>().unwrap();

                    if acode.len() != 6 {
                        let content = "审核码不正确";
                        let r = mp::TextReply {
                            from_user_name: to_user_name,
                            to_user_name: from_user_name,
                            create_time: ts,
                            content: content.to_owned(),
                        };

                        let rr = mp::Reply::TextReply(r).render();

                        return Ok(rr);
                    }
                    let client = &state.rs;
                    let acode = format!("acode-{}", acode);
                    let isbn = redisx::get(client, &acode).await.unwrap_or("".to_string());
                    if isbn == "" {
                        let content = "审核码已失效";
                        let r = mp::TextReply {
                            from_user_name: to_user_name,
                            to_user_name: from_user_name,
                            create_time: ts,
                            content: content.to_owned(),
                        };

                        let rr = mp::Reply::TextReply(r).render();

                        return Ok(rr);
                    }

                    let pool = &state.db;
                    let _result = sql::audit_book_by_isbn(pool, isbn, status).await?;
                    let _ = redisx::del(client, &acode).await?;
                    let r = mp::TextReply {
                        from_user_name: to_user_name,
                        to_user_name: from_user_name,
                        create_time: ts,
                        content: "操作成功".to_string(),
                    };

                    let rr = mp::Reply::TextReply(r).render();

                    return Ok(rr);
                }
            }
            mp::Message::ImageMessage(_) => {
                tracing::debug!("image message not support");
            }
            mp::Message::EventMessage(m) => {
                if m.msg_type.as_str() == "event" {
                    if m.event.as_str() == "subscribe" {
                        tracing::debug!("user subscribe");
                        // 查看用户是否入库？
                        let pool = &state.db;
                        let cnt = sql::query_user_count(&pool, params.openid.clone()).await?;
                        if cnt == 0 {
                            let rand_str = utils::get_rand_str(6);
                            let nickname = format!("mu{}", rand_str.to_lowercase());
                            sql::create_user(&pool, params.openid.clone(), nickname).await?;
                        }

                        // 发送一个企业微信机器人通知
                        let content = format!("关注公众号，{}", utils::get_currdate());
                        utils::send_mp_notify(&conf.mp.robot_url, content).await?;
                        let from_user_name = m.from_user_name;
                        let to_user_name = m.to_user_name;
                        let ts = utils::get_timestamp();
                        let welcome = String::from("欢迎您！\n支持以下命令：\n验证码\n识别码\n");
                        let r = mp::TextReply {
                            from_user_name: to_user_name,
                            to_user_name: from_user_name,
                            create_time: ts,
                            content: welcome,
                        };

                        let rr = mp::Reply::TextReply(r).render();

                        return Ok(rr);
                    } else if m.event.as_str() == "unsubscribe" {
                        let pool = &state.db;
                        sql::remove_user(&pool, params.openid.clone()).await?;
                        let content = format!("取关公众号，{}", utils::get_currdate());
                        utils::send_mp_notify(&conf.mp.robot_url, content).await?;
                    }
                }
            }
            mp::Message::UnSupportMessage(_) => {
                tracing::debug!("other message not support");
            }
        }
    }
    // 默认发送这个，公众号不再重试
    Ok("success".to_string())
}
